package com.hishop.payment.service.impl;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.google.common.collect.Maps;
import com.hishop.payment.dao.PayInfoDao;
import com.hishop.payment.domain.Constant;
import com.hishop.payment.domain.Pager;
import com.hishop.payment.domain.PayInfo;
import com.hishop.payment.domain.PayInfoVo;
import com.hishop.payment.service.PayInfoService;
import com.hishop.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 *
 * @author hejie 2017年11月22日 下午2:42:02
 */
@Service
public class PayInfoServiceImpl implements PayInfoService {
	final static Logger log = LoggerFactory.getLogger(PayInfoServiceImpl.class);
	@Autowired
	private PayInfoDao payInfoDao;
	private JsonUtil jsonUtil = new JsonUtil();
	private static AlipayClient alipayClient;
	static{
	//SDK调用前需要进行初始化,alipayClient只需要初始化一次，后续调用不同的API都可以使用同一个alipayClient对象
	alipayClient = new DefaultAlipayClient(Constant.ALIPAY_URL, Constant.ALIPAY_APP_ID, Constant.ALIPAY_APP_PRIVATE_KEY,
                   Constant.FORMAT, Constant.CHARSET, Constant.ALIPAY_PUBLIC_KEY, Constant.SIGN_TYPE);
	}
	
	 @Override
	public String getPreparePayInfo(String studentId,String packageId,String months,String recommendCode,String price,String name,String description) {
		Map<String, Object> reMap = new HashMap<String, Object>();
		boolean isTrue = checkGetPreparePayInfoArg(studentId, packageId, months, recommendCode, price, name);
		if(!isTrue){
			reMap.put("ret", 0);
			reMap.put("msg", "校验生成预支付订单信息客户端传参错误!");
			log.info("校验生成预支付订单信息客户端传参:" + jsonUtil.obj2String(reMap));
			return jsonUtil.obj2String(reMap);
		}
		String orderNo=generateOrderNo();
		int result = payInfoDao.addPayInfo(Long.parseLong(studentId),orderNo,packageId,months,recommendCode,new BigDecimal(price),name,description);
		if(result>0){
			reMap.put("ret", 1);
			reMap.put("msg", "生成预支付订单信息成功!");
		}else{
			reMap.put("ret", 0);
			reMap.put("msg", "生成预支付订单信息失败!");
		}
		log.info("生成预支付订单信息:" + jsonUtil.obj2String(reMap));
		reMap.putAll(payOrder(studentId,orderNo));
		return jsonUtil.obj2String(reMap);
	}
		
	@Override
	public String getPayStatus(String orderid) {
		Map<String, Object> reMap = new HashMap<String, Object>();
		PayInfo payInfo = payInfoDao.getPayInfo(orderid);
		if(payInfo!=null){
			reMap.put("ret", 0);
			PayInfoVo payInfoVo=new PayInfoVo();
			BeanUtils.copyProperties(payInfo, payInfoVo);
			reMap.put("data", payInfoVo);
		}else{
			reMap.put("ret", 1);
			reMap.put("msg", "查询支付结果信息失败!");
		}
		return jsonUtil.obj2String(reMap);
	}
	
	@Override
	public String getOrderList(String studentId, String orderNo, int pageSize,int page) {
		Map<String, Object> reMap = new HashMap<String, Object>();
		Pager pager = payInfoDao.getOrderList(studentId,orderNo,pageSize,page);
		int count = pager.getPageCount();
		if(page>count){
			reMap.put("data", new ArrayList<PayInfo>());
		}
		return null;
	}
	
	@Override
	public Map<String, Object> payOrder(String studentId, String orderNo) {
		Map<String, Object> reMap = new HashMap<String, Object>();
		PayInfo payInfo = payInfoDao.getPayInfo(orderNo);
		//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
		AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
		//SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
		AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
		model.setBody(payInfo.getName());
		model.setSubject(payInfo.getName());
		model.setOutTradeNo(orderNo);
		model.setTimeoutExpress("30m");
		/**支付宝约定：订单总金额，单位为元，精确到小数点后两位，取值范围[0.01,100000000]*/
		model.setTotalAmount(getTotalAmount(payInfo.getPrice()));
		model.setProductCode("QUICK_MSECURITY_PAY");
		request.setBizModel(model);
		request.setNotifyUrl(Constant.ALIPAY_CALLBACK_URL);
		try {
		   //这里和普通的接口调用不同，使用的是sdkExecute
			
		   AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
		   System.out.println(response.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。
		   reMap.put("ret", 0);
		   reMap.put("msg", response.getBody());
		   reMap.put("orderid", orderNo);
		   reMap.put("order_string",response.getBody());
		} catch (AlipayApiException e) {
			log.info("AlipayApiException异常信息ErrCode:{},LocalizedMessage:{},Message:{},StackTrace:{}",e.getErrCode(),e.getLocalizedMessage(),e.getMessage(),e.getStackTrace());
			reMap.put("ret", 1);
		    reMap.put("msg", e.getErrMsg());
		}
		return reMap;
	}

	@Override
	public String aliCallback(Map<String, String[]> requestParams) {
        Map<String,String> params = Maps.newHashMap();
        for (Iterator<?> iter=requestParams.keySet().iterator();iter.hasNext();){
             String name= (String) iter.next();
             String[]values= (String[]) requestParams.get(name);
             String  valueStr= StringUtils.EMPTY;
             for (int i=0;i<values.length;i++){
                 valueStr=(i==values.length-1)?valueStr+values[i]:valueStr+values[i]+",";
             }
            params.put(name,valueStr);
        }
	 
	  String order_id = params.get("out_trade_no");//原支付请求的商户订单号
	  String trade_no = params.get("trade_no");//支付宝交易凭证号
	  String trade_status = params.get("trade_status");//支付宝交易状态
	  boolean alipayRSACheckedV1 = false;
	  try {
		//异步通知验签,是不是来自支付宝
		alipayRSACheckedV1 = AlipaySignature.rsaCheckV1(params, Constant.ALIPAY_PUBLIC_KEY,Constant.CHARSET,Constant.SIGN_TYPE);
	   } catch (AlipayApiException e1) {
		   log.info("支付宝验签异常："+e1.getMessage());
		   String responseResult = "{'ret':'1','msg':'订单:'"+order_id+"支付宝验签异常!}";
		   payInfoDao.updatePayInfo(responseResult,order_id);
		   return "success";
	   }
	   //问题：考虑来自网页支付宝支付怎么验签
	   if(!alipayRSACheckedV1){
		   String responseResult = "{'ret':'1','msg':'订单:'"+order_id+"支付宝验签失败!}";
		   payInfoDao.updatePayInfo(responseResult,order_id);
		   return "success";
		}
		
		PayInfo payInfo = payInfoDao.getPayInfo(order_id);
		if(null == payInfo){
			String responseResult = "{'ret':'1','msg':'订单:'"+order_id+"非上海和校园“逗你学”的订单,回调忽略!}";
			payInfoDao.updatePayInfo(responseResult,order_id);
			return "success";
		}
		//防止支付宝重复调用
		if(payInfo.getStatus() >= Constant.ORDER_PAID){
			String responseResult = "{'ret':'1','msg':'订单:'"+order_id+"支付宝重复调用!}";
			payInfoDao.updatePayInfo(responseResult,order_id);
			return "success";
		}
		//如果交易成功，更改订单状态为已付款
		if(Constant.TRADE_STATUS_TRADE_SUCCESS.equals(trade_status)){
			String payTimeStr = params.get("gmt_payment");//交易付款时间
			String responseResult = "{支付宝交易凭证号:"+trade_no+"}";
			Date payTime;
			try {
				payTime = Str2Date(payTimeStr);
			} catch (ParseException e) {
				log.info("交易付款时间解析异常:"+e.getMessage());
				payTime=new Date();//如果回调的交易时间无法解析成日期，默认使用当前系统时间
			}
		    payInfoDao.updatePayInfo(Constant.ORDER_PAID,payTime,responseResult,order_id);
		    return "success";
		}
		//如果交易取消或交易失败等其他情况，记录返回值信息
		String responseResult = "{'ret':'1','msg':'订单:'"+order_id+trade_status+"}";
		payInfoDao.updatePayInfo(responseResult,order_id);
		    return "success";	
	}
	
	/**
	 * 支付宝订单总金额规则
	 * 四舍五入保留两位数，取值范围[0.01,100000000]
	 * @param price
	 * @return
	 */
	private static String getTotalAmount(BigDecimal price){
		//取值范围[0.01,100000000]
		if(price.compareTo(new BigDecimal("0.01")) == -1 || price.compareTo(new BigDecimal("100000000")) == 1){
			throw new IllegalArgumentException("请求参数错误：订单总金额取值范围只能0.01~100000000元之间");
		}
		return String.valueOf(price.setScale(2, BigDecimal.ROUND_HALF_UP));
	}
	
    /**
     * 字符串转时间
     * @throws ParseException 
     */
	private Date Str2Date(String timeStr) throws ParseException{
		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.parse(timeStr);
	}
	
	/**
	 * 校验生成预支付订单信息客户端传参
	 * @return
	 */
	private boolean checkGetPreparePayInfoArg(String studentId,String packageId,String months,String recommendCode,String price,String name){
		if(StringUtils.isBlank(studentId) || !StringUtils.isNumeric(studentId)){
			return false;
		}
		if(StringUtils.isBlank(packageId) || !StringUtils.isNumeric(packageId)){
			return false;
		}
		if(StringUtils.isBlank(months) || StringUtils.isBlank(recommendCode) || StringUtils.isBlank(name)){
			return false;
		}
		try{
			new BigDecimal(price); //如果不是数字字符会抛异常
		}catch(NumberFormatException e){
			return false;
		}
		return true;
	}
	
     /**
      * 订单号生成规则：UUID+时间戳
      * 订单号最大长度为64位
      * @return
      */
	private String generateOrderNo() {
		  String uuid = UUID.randomUUID().toString().replaceAll("-", "");
		  String orderNo=uuid + System.currentTimeMillis();
		  if(orderNo.length()>64){
			  orderNo=orderNo.substring(orderNo.length()-64);
		  }
		  log.info("订单号长度："+orderNo.length());
		return orderNo;
	}
}
 